package code;

import java.util.LinkedList;

public class Kernel {
//---- Fields
private LinkedList processes = new LinkedList();                // Bendras visu, sistemoje esanciu, procesu sarasas
private LinkedList resources = new LinkedList();                // Bendras visu, sistemoje esanciu, resursu sarasas
private LinkedList readyProc = new LinkedList();                // Pasiruosusiu procesu sarasas
private Process runProc = new Process();                        // Einamas procesas
private LinkedList blockedProc = new LinkedList();              // Blokuoti procesai (del input'o)
private LinkedList stoppedProc = new LinkedList();              // Sustabdyti procesai
private RealMachine realMachine;                                // Nuoroda i realia masina
private LinkedList vMachines = new LinkedList();                // VM masyvas

//---- Methods
/**
 * Constructor
 * @param rm
 */
public Kernel(RealMachine rm)
{
    this.realMachine = rm;

    resources.add(new Resource(null, ResourceType.input));
}

/**
 * Suzinom vykdoma procesa
 * @return runProc
 */
public Process getRunProc()
{
    return this.runProc;
}

/**
 * Suzinom visus procesus
 * @return processes
 */
public LinkedList getProcesses()
{
    return this.processes;
}

/**
 * Suzinom blokuotus procesus
 * @return blockedProc
 */
public LinkedList getBlockedProc()
{
    return this.blockedProc;
}

/**
 * Suzinom sustabdytus procesus
 * @return stoppedProc
 */
public LinkedList getStoppedProc()
{
    return this.stoppedProc;
}

/**
 * Suzinom pasiruosusius procesus
 * @return readyProc
 */
public LinkedList getReadyProc()
{
    return this.readyProc;
}

/**
 * Suzinom virtualias masinas
 * @return vMachines
 */
public LinkedList getVMachines()
{
    return this.vMachines;
}

/**
 * Kuriam VM
 * @param id
 */
public void createVM(int id)
{
    //prasyti atminties ir ja perduoti vm
    vMachines.addLast(new VirtualMachine(id, this.realMachine));
}

///////////////Resursu primityvai /////////////
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\//
// Resursu paskirstytojas integruotas i resursu primityvus
/**
 * Prasyti resurso
 * @param id
 */
public void requestInput(int id)
{
    blockProcess(id);    //blokuojam procesa
    grantResource();
}

/**
 * Dalyti resursa
 */
public void grantResource()
{
    boolean bool = false;

    //perziurim ar naudojamas input'as
    for (int i = 0; i < processes.size(); i++)
    {
        if (((Process) processes.get(i)).res != null)
        {         // cia jei niekas nenaudoja jokio resurso, o ne inputo. hm to ir reik?
            // gal kiti net nefiksuojami, sitemoje figuruoja tik input tipo resursas?
            bool = true;
            System.out.println("iddd" + ((Process) processes.get(i)).getId());
            System.out.println("resss" + ((Process) processes.get(i)).res);
        }
    }

    //jei nenaudojamas ir sukurtas
    if (!bool && realMachine.input.size() > 0)
    {
        //cia yra pasiruose
        readyProc.add(blockedProc.poll());
        ((Process) readyProc.peek()).setResource(ResourceType.input);
    }
}

/**
 * Atlaisvinti resursa
 * @param id
 * @param type
 */
public void returnResource(int id)
{
    for (int i = 0; i < processes.size(); i++)
    {
        if (((Process) processes.get(i)).getId() == id)
            ((Process) processes.get(i)).res = null;
    }
    grantResource();
}

/**
 * Planuotojas
 */
public void planner()
{

    //jei einamasis procesas nera blokuotas, jis itraukiamas i pasiruosusiu proc sarasa
    if (runProc.state == ProcessState.run)
        readyProc.addLast(runProc);

    //jei yra pasiruosusiu procesu
    if (readyProc.size() > 0)
    {
        //imamas pirmas is eiles
        Process tmp = new Process();    // nu ka, neuzteko "Process tmp;"  ? ;]
        tmp = (Process) readyProc.getFirst();
        //valdymo perdavimas:

        if (runProc.state != null)
        {
            //1)issaugom einamojo proceso registrus proceso aprase
            if (runProc.state != ProcessState.stop)
                saveRegisters(runProc);
            //2)proceso aprase laikoma CPU busena priskiriama realiam procesoriui
            restoreRegisters(tmp);
        }
        //naujas procesas pazymimas kaip einamasis
        activeProcess(((Process) readyProc.getFirst()).getId());
        readyProc.removeFirst();
    }
}

/**
 * Issaugom procesoriaus registru reiksmes proceso aprase
 */
public void saveRegisters(Process proc)
{
    proc.savedRegisters.PTR = realMachine.intToHexWord(realMachine.getPTR());
    proc.savedRegisters.IP = realMachine.intToHexWord(realMachine.getIP());
    proc.savedRegisters.C = realMachine.getC();
    proc.savedRegisters.CS = realMachine.intToHexWord(realMachine.getCS());
    proc.savedRegisters.DS = realMachine.intToHexWord(realMachine.getDS());
    proc.savedRegisters.SS = realMachine.intToHexWord(realMachine.getSS());
    proc.savedRegisters.SP = realMachine.intToHexWord(realMachine.getSP());

    proc.savedRegisters.PI = realMachine.getPI();
    proc.savedRegisters.SI = realMachine.getSI();
    proc.savedRegisters.TI = realMachine.getTI();
}

/**
 * Atstatom procesoriaus registru reiksmes is proceso apraso
 */
public void restoreRegisters(Process proc)
{
    realMachine.setPTR(realMachine.hexToDec(realMachine.wordToHexString(proc.savedRegisters.PTR)));
    realMachine.setIP(realMachine.hexToDec(realMachine.wordToHexString(proc.savedRegisters.IP)));
    realMachine.setC(proc.savedRegisters.C);
    realMachine.setCS(realMachine.hexToDec(realMachine.wordToHexString(proc.savedRegisters.CS)));
    realMachine.setDS(realMachine.hexToDec(realMachine.wordToHexString(proc.savedRegisters.DS)));
    //realMachine.setDS(realMachine.wordToInt(proc.savedRegisters.DS));   //no good, nes word(80) -> int(80). turi but int(128)

    realMachine.setSS(realMachine.hexToDec(realMachine.wordToHexString(proc.savedRegisters.SS)));
    realMachine.setSP(realMachine.hexToDec(realMachine.wordToHexString(proc.savedRegisters.SP)));
    realMachine.setPI(proc.savedRegisters.PI);
    realMachine.setSI(proc.savedRegisters.SI);
    realMachine.setTI(proc.savedRegisters.TI);
}

/* procesu primityvai */
/**
 * Kurti procesa
 * @param id
 * @param priority
 * @param name
 * @param vm
 */
public void createProcess(int id, int priority, VirtualMachine vm)
{
    //pridedam prie proc visu saraso
    processes.add(new Process(id, priority, vm));
    //pridedam prie blokuotu proc saraso
    Object tmp = processes.getLast();
    this.readyProc.add(tmp);
}

/**
 * Naikinti procesa
 * @param id
 */
public void killProcess(int id)
{
    for (int i = 0; i < processes.size(); i++)
    {
        if (((Process) processes.get(i)).getId() == id)
            processes.remove(processes.get(i));
    }
}

/**
 * Blokuoti procesa
 * @param id
 */
public void blockProcess(int id)
{
    for (int i = 0; i < processes.size(); i++)
    {
        if (((Process) processes.get(i)).getId() == id)
        {
            ((Process) processes.get(i)).state = ProcessState.block;
            blockedProc.add(processes.get(i));
        }
    }
}

/**
 * Aktyvuoti procesa
 * @param id
 */
public void activeProcess(int id)
{
    for (int i = 0; i < processes.size(); i++)
    {
        if (((Process) processes.get(i)).getId() == id)
        {
            runProc = (Process) processes.get(i);
            runProc.state = ProcessState.run;
            realMachine.setMODE('u');
            restoreRegisters(runProc);
            realMachine.setTI(10);
        }
    }
}

/**
 * Patikrinam ar nekilo partraukimas arba apklausiam ir isvalom konkretu registra
 * @param x
 */
public int test(int x)
{
    int tmp;

    if (x == 1)
    {
        tmp = realMachine.getPI();
        realMachine.setPI(0);
        return tmp;
    } else if (x == 2)
    {
        tmp = realMachine.getSI();
        realMachine.setSI(0);
        return tmp;
    } else if (x == 3)
    {
        tmp = realMachine.getTI();
        realMachine.setTI(0);
        return tmp;
    } else if ((realMachine.getPI() + realMachine.getSI()) > 0 || realMachine.getTI() == 0)
        return 1;
    else
        return 0;
}

/**
 * Pertraukimu apdorojimas
 */
public void interruptHandling()
{
    saveRegisters(runProc);      //issaugom registru reiksmes
    realMachine.setMODE('s');    //perjungiam i supervizorini rezima

    //nustatom pertraukimo tipa
    if (realMachine.getPI() != 0)
    {
        runProc.state = ProcessState.stop;                  //busena sustabdytas
        stoppedProc.add(runProc);
        ((VirtualMachine) runProc.getVM()).setStop(true);    //sustabdom VM
        if (test(1) == 1)
        {
        //1 - atm apsauga
        } else
        {
        //2 - neleistinas OPK
        }
    } else if (realMachine.getSI() != 0)
    {
        int tmp = test(2);
        if (tmp == 1)
        {
            //GD
            //reikalaujam input'o
            requestInput(runProc.getId());
            planner();
        } else if (tmp == 2)
        {
        //PD
        //isvedam i output'a ir tesiam darba toliau
        } else if (tmp == 3)
        {
            //HL
            runProc.state = ProcessState.stop;                  //busena sustabdytas
            ((VirtualMachine) runProc.getVM()).setStop(true);    //sustabdom VM
            stoppedProc.add(runProc);
            planner();
        }
    } else if (test(3) == 0)
    {
        //taimerio
        runProc.state = ProcessState.ready;    //busena sustabdytas
        readyProc.add(runProc);
        planner();
    }
}

/**
 * Vykdom visa programa iskart
 */
public void runOS()
{

    String tmp = null;
    int realAdr = 0;
    int adr = 0;
    String operand = null;

    short x = 0, y = 0;

    realMachine.setIP(0);    //pirma gaunam pirmos komandos adresa
    realMachine.setMODE('u');

    //planner();

    if (runProc.state == ProcessState.block)
    {
        grantResource();
        planner();
    }
    if (runProc.state == ProcessState.stop && !isOsComplete())
    {
        grantResource();
        planner();
    }

    while ((!isOsComplete()) && (runProc.getState() != ProcessState.stop) && (readyProc.size() > 0 || (processes.size() != blockedProc.size())))
    {
//			realAdr = (short)(realMachine.getCS() + realMachine.getIP());
//			tmp = String.valueOf(realMachine.memory[realAdr].a) + String.valueOf(realMachine.memory[realAdr].b) + String.valueOf(realMachine.memory[realAdr].c) + String.valueOf(realMachine.memory[realAdr].d);

        adr = (realMachine.getCS() + realMachine.getIP());
        realAdr = (realMachine.wordToInt(realMachine.memory[realMachine.getPTR() * 256]) + adr);
        tmp = String.valueOf(realMachine.memory[realAdr].a) + String.valueOf(realMachine.memory[realAdr].b) +
                String.valueOf(realMachine.memory[realAdr].c) + String.valueOf(realMachine.memory[realAdr].d);


        if (tmp.compareTo("HALT") != 0)
        {
            tmp = String.valueOf(realMachine.memory[realAdr].a) + String.valueOf(realMachine.memory[realAdr].b) +
                    String.valueOf(realMachine.memory[realAdr].c) + String.valueOf(realMachine.memory[realAdr].d);
            operand = String.valueOf(realMachine.memory[realAdr + 1].a) + String.valueOf(realMachine.memory[realAdr + 1].b) +
                    String.valueOf(realMachine.memory[realAdr + 1].c) + String.valueOf(realMachine.memory[realAdr + 1].d);

//				tmp = String.valueOf(realMachine.memory[realAdr].a) + String.valueOf(realMachine.memory[realAdr].b);
//				x = Short.valueOf(String.valueOf(realMachine.memory[realAdr].c));
//				y = Short.valueOf(String.valueOf(realMachine.memory[realAdr].d));

            if (realMachine.hexToDec(operand) > RealMachine.Block_size / 2)
            {
                realMachine.setPI(1);
                interruptHandling();
            } else
            {
                try
                {
                    //isverciam instrukcija i RM kalba ir ivykdom
                    realMachine.executeCommands(OPC.valueOf(tmp), realMachine.hexToDec(operand));
                    //realMachine.executeCommands(translate(OPC.valueOf(tmp), x, y));

                    //atlaisvinam resursa, jei buvo ivedimas
                    if (OPC.valueOf(tmp) == OPC.GD__)
                        returnResource(runProc.getId());
                } catch (Exception e)
                {
                    //neleistinas OPC
                    realMachine.setPI(2);
                }

                saveRegisters(runProc);

                //tikrinam ar nekilo pertraukimas
                if (test(0) == 1)
                    interruptHandling();
            }
        }
    }
}
/**
 * OS vykdom pazingsniui
 */
int baba = 0;

public void traceOS()
{
    String tmp = null;
    int realAdr = 0;
    int adr = 0;
    String operand = null;

    realMachine.setMODE('u');

    if (runProc.state == ProcessState.block)
    {
        grantResource();
        planner();
    }
    if (runProc.state == ProcessState.stop && !isOsComplete())
    {
        grantResource();
        planner();
    }

    if (runProc.state != ProcessState.stop && runProc.state != ProcessState.block)
    {
//			realAdr = (short)(realMachine.getCS() + realMachine.getIP());
//			tmp = String.valueOf(realMachine.memory[realAdr].a) + String.valueOf(realMachine.memory[realAdr].b) + String.valueOf(realMachine.memory[realAdr].c) + String.valueOf(realMachine.memory[realAdr].d);
        adr = (realMachine.getCS() + realMachine.getIP());
        //      realAdr = realMachine.wordToInt(realMachine.memory[realMachine.getPTR() * 256]) + adr;
        realAdr = realMachine.getRealAddress(adr);
        tmp = String.valueOf(realMachine.memory[realAdr].a) + String.valueOf(realMachine.memory[realAdr].b) +
                String.valueOf(realMachine.memory[realAdr].c) + String.valueOf(realMachine.memory[realAdr].d);
   //     System.out.println("com'as" + tmp.toString() + "cmp");
        if (tmp.compareTo("HALT") != 0)
        {
            baba++;

            tmp = String.valueOf(realMachine.memory[realAdr].a) + String.valueOf(realMachine.memory[realAdr].b) +
                    String.valueOf(realMachine.memory[realAdr].c) + String.valueOf(realMachine.memory[realAdr].d);
            operand = String.valueOf(realMachine.memory[realAdr + 1].a) + String.valueOf(realMachine.memory[realAdr + 1].b) +
                    String.valueOf(realMachine.memory[realAdr + 1].c) + String.valueOf(realMachine.memory[realAdr + 1].d);
//				tmp = String.valueOf(realMachine.memory[realAdr].a) + String.valueOf(realMachine.memory[realAdr].b);
//				x = Short.valueOf(String.valueOf(realMachine.memory[realAdr].c));
//				y = Short.valueOf(String.valueOf(realMachine.memory[realAdr].d));

            if (realMachine.hexToDec(operand) > RealMachine.Block_size / 2)
            {
                realMachine.setPI(1);
                interruptHandling();
            } else
            {
                try
                {
                    //isverciam instrukcija i RM kalba ir ivykdom
                    realMachine.executeCommands(OPC.valueOf(tmp), realMachine.hexToDec(operand));
                    //realMachine.executeCommands(translate(OPC.valueOf(tmp), x, y));

                    //atlaisvinam resursa, jei buvo ivedimas
                    if (OPC.valueOf(tmp) == OPC.GD__)
                        returnResource(runProc.getId());
                } catch (Exception e)
                {
                    //neleistinas OPC
                    realMachine.setPI(2);
                }

                saveRegisters(runProc);

                //tikrinam ar nekilo pertraukimas
                if (test(0) == 1)
                    interruptHandling();
            }
        }
    }
}

/**
 * Isvercia OPC is VM instrukciju i RM instrukcijas
 */
//	public short translate(OPC opc, short x, short y) {
//		switch(opc) {
//		case LR:
//			return (short)(11 * 100 + x * 10 + y);
//		case SR:
//			return (short)(12 * 100 + x * 10 + y);
//		case AD:
//			return (short)(13 * 100 + x * 10 + y);
//		case CR:
//			return (short)(14 * 100 + x * 10 + y);
//		case BT:
//			return (short)(15 * 100 + x * 10 + y);
//		case NT:
//			return (short)(16 * 100 + x * 10 + y);
//		case GO:
//			return (short)(17 * 100 + x * 10 + y);
//		case GD:
//			return (short)(18 * 100 + x * 10 + y);
//		case PD:
//			return (short)(19 * 100 + x * 10 + y);
//		case NO:
//			return (short)1000;
//		case HL:
//			return (short)2000;
//		default:
//			return (short)0;
//		}
//	}
/**
 * Patikrinam ar ivykdytos visos uzduotys
 * @return false - jei ne, true - jei taip
 */
public boolean isOsComplete()
{
    if (processes.size() == stoppedProc.size())
        return true;
    else
        return false;
}
}
